package com.enation.app.javashop.core.goods.service;

import com.enation.app.javashop.core.goods.model.dos.GoodsDO;
import com.enation.app.javashop.core.goods.model.dos.GoodsSkuDO;
import com.enation.app.javashop.core.goods.model.enums.QuantityLogType;
import com.enation.app.javashop.core.goods.model.vo.CacheGoods;
import com.enation.app.javashop.core.goods.model.vo.GoodsQuantityVO;
import com.enation.app.javashop.framework.cache.Cache;
import com.enation.app.javashop.framework.database.DaoSupport;
import net.sourceforge.groboutils.junit.v1.MultiThreadedTestRunner;
import net.sourceforge.groboutils.junit.v1.TestRunnable;
import org.junit.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

/**
 * 并发扣减库存测试手动回滚测试
 * @author kingapex
 * @version 1.0
 * @since 7.0.0
 * 2018/6/6
 */
@Transactional(value = "goodsTransactionManager", propagation = Propagation.NOT_SUPPORTED,rollbackFor = Exception.class)
public class ConcurrencyQuantityManualRollback extends QuantityDataInitiator {


    @Autowired
    private DaoSupport goodsDaoSupport;

    @Autowired
    private Cache cache;


    @Autowired
    private GoodsQuantityManager quantityManager;

    /**
     * 订单编号
     */
    private String orderSn = "0001";


    /**
     * 要扣减的库存
     */
    private List<GoodsQuantityVO> goodsQuantityList = new ArrayList<>();

    /**
     * 原始的商品数据
     */
    private List<CacheGoods> originList = new ArrayList();


    /**
     * 初始化回滚数据
     * 库存全部充足，每商品，每sku100个
     * 每个商品买一个
     */
    @Before
    public void initRollbackData() {

        init();


        int i = 0;
        int goodsEnableQuantity = 100;
        int skuEnableQuantity = 100;
        for (GoodsDO goods : goodsList) {

            Integer goodsId = goods.getGoodsId();


            goodsDaoSupport.execute("update es_goods set enable_quantity=?,quantity=? where goods_id=?", goodsEnableQuantity, goodsEnableQuantity, goodsId);
            goodsDaoSupport.execute("update es_goods_sku set  enable_quantity=?,quantity=? where goods_id=?", skuEnableQuantity, skuEnableQuantity, goodsId);


            List<GoodsSkuDO> goodsSkuList = this.findGoodsSku(goodsId);

            GoodsSkuDO sku = goodsSkuList.get(0);
            GoodsQuantityVO goodsQuantity1 = new GoodsQuantityVO();
            goodsQuantity1.setGoodsId(goodsId);
            goodsQuantity1.setSkuId(sku.getSkuId());
            goodsQuantity1.setEnableQuantity(1);
            goodsQuantityList.add(goodsQuantity1);

            i++;
        }


    }


    /**
     * 手动回滚测试
     */
    @Test
    public void rollbackTest() {

        //50个线程并发
        //每个sku减一个，一个商品1个sku
        TestRunnable[] trs = new TestRunnable[50];


        //前30个线程扣减
        for (int i = 0; i < 30; i++) {
            trs[i] = new QuantityTask(i);
        }
        int index = 0;

        //后20个线程回滚
        for (int i = 30; i < trs.length; i++) {
            trs[i] = new QuantityRollbackTask(index);
            index++;
        }

        MultiThreadedTestRunner runner = new MultiThreadedTestRunner(trs);
        try {

            runner.runTestRunnables();
        } catch (Throwable e) {
            e.printStackTrace();
        }

        //断言
        rollbackAssert();
    }

    /**
     * 回滚断言
     */
    private void rollbackAssert() {
        int i = 0;

        //商品库存断言
        for (GoodsDO goods : goodsList) {

            Integer quantityNum = goodsDaoSupport.queryForInt("select enable_quantity from es_goods where goods_id=?", goods.getGoodsId());

            Integer expectQuantityNum = 70;

            Assert.assertEquals(expectQuantityNum, quantityNum);

            i++;
        }


        //sku库存断言
        i=0;
        for (GoodsQuantityVO quantityVO : goodsQuantityList){
            Integer skuQuantity = goodsDaoSupport.queryForInt("select enable_quantity from es_goods_sku where sku_id=?", quantityVO.getSkuId());
            Integer expectQuantityNum = 70;
            Assert.assertEquals(expectQuantityNum, skuQuantity);

            i++;
        }

    }


    class QuantityTask extends TestRunnable {
        int i = 0;

        public QuantityTask(int i) {
            this.i = i;
        }

        @Override
        public void runTest() throws Throwable {
            quantityManager.reduceGoodsQuantity(orderSn + "_" + i, QuantityLogType.ORDER_CREATE ,goodsQuantityList);
        }

    }

    class QuantityRollbackTask extends TestRunnable {
        int i = 0;

        public QuantityRollbackTask(int i) {
            this.i = i;
        }

        @Override
        public void runTest() throws Throwable {
            quantityManager.rollbackReduce(orderSn + "_" + i);
        }

    }


    /**
     * 清除数据
     */
    @After
    public void clear(){
        clearTestData();
    }



}


